/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

using System;

using MetaDefinition = argot.meta.MetaDefinition;

using ByteArrayCompare = argot.util.ByteArrayCompare;
using TypeMapCore = argot.TypeMapCore;


namespace argot.message
{
	/// <summary>
	/// Summary description for MessageReader.
	/// </summary>
	public class MessageReader
	{
		/// <summary> 
		/// Read a dictionary from a stream.  expects full meta dictionary, dictionary, map format.
		/// </summary>
		public static System.Object ReadMessage(TypeLibrary library, System.IO.Stream fis)
		{
			TypeMapCore refCore = TypeMapCore.GetCoreTypeMap(library);
			refCore.Map(22, library.GetId("dictionary.map"));
			refCore.Map(23, library.GetId("dictionary.words"));
			refCore.Map(24, library.GetId("dictionary.definition"));
			refCore.Map(25, library.GetId("dictionary.entry"));
			refCore.Map(26, library.GetId("meta.envelop"));
			refCore.Map(27, library.GetId("meta.definition#envelop"));
			
			TypeMapCore core = TypeMapCore.GetCoreTypeMap(library, refCore);
			core.Map(22, library.GetId("dictionary.map"));
			core.Map(23, library.GetId("dictionary.words"));
			core.Map(24, library.GetId("dictionary.definition"));
			core.Map(25, library.GetId("dictionary.entry"));
			core.Map(26, library.GetId("meta.envelop"));
			core.Map(27, library.GetId("meta.definition#envelop"));
			
			TypeInputStream tmis = new TypeInputStream(fis, core);
			
			
			// read the core map.
			ReferenceTypeMap coreMap = ReadCore(library, tmis);
			// read the message map using the core map.
			
			ReferenceTypeMap messageMap = ReadMessageMap(tmis, coreMap);
			// read the final dictionary. register types if needed.
			System.Collections.IEnumerator iter = messageMap.Iterator;
			while (iter.MoveNext())
			{
				System.Int32 i = (System.Int32) iter.Current;
			}
			
			System.UInt16 ident = (System.UInt16) tmis.ReadObject("u16");
			TypeInputStream tis = new TypeInputStream( fis, messageMap );
			return tis.ReadObject( ident );
		}
		
		private static ReferenceTypeMap ReadCore(TypeLibrary library, TypeInputStream tmis)
		{
			TypeMapCore refCore = TypeMapCore.GetCoreTypeMap(library);
			refCore.Map(22, library.GetId("dictionary.map"));
			refCore.Map(23, library.GetId("dictionary.words"));
			refCore.Map(24, library.GetId("dictionary.definition"));
			refCore.Map(25, library.GetId("dictionary.entry"));
			refCore.Map(26, library.GetId("meta.envelop"));
			refCore.Map(27, library.GetId("meta.definition#envelop"));
			
			TypeMapCore core = TypeMapCore.GetCoreTypeMap(library, refCore);
			core.Map(22, library.GetId("dictionary.map"));
			core.Map(23, library.GetId("dictionary.words"));
			core.Map(24, library.GetId("dictionary.definition"));
			core.Map(25, library.GetId("dictionary.entry"));
			core.Map(26, library.GetId("meta.envelop"));
			core.Map(27, library.GetId("meta.definition#envelop"));
			
			// read the core array size.  expect = 2.
			System.Byte arraySize = (System.Byte) tmis.ReadObject("u8");
			
			byte[] readcore = (byte[]) tmis.ReadObject("dictionary.words");
			byte[] localCore = WriteCore(core);
			// compare the core read with my own core.

			if (!ByteArrayCompare.Compare(readcore, localCore))
			{
				for (int x = 0; x < readcore.Length; x++)
				{
					if (readcore[x] != localCore[x])
					{
						System.Console.Out.WriteLine("no match at pos: " + x + "," + readcore[x] + "," + localCore[x]);
					}
				}
				throw new TypeException("core dictionaries did not match");
			}
			
			// now the core is confirmed we can add in the extensions.
			for (int x = 0; x < (int) arraySize - 1; x++)
			{
				byte[] extension = (byte[]) tmis.ReadObject("dictionary.words");
				System.IO.MemoryStream bais = new System.IO.MemoryStream(extension);
				TypeInputStream extDataIn = new TypeInputStream(bais, core);
				
				SetMap(extDataIn, core, core);
			}
			
			return core;
		}
		
		private static ReferenceTypeMap ReadMessageMap(TypeInputStream tmis, ReferenceTypeMap coreMap)
		{
			ReferenceTypeMap mapSpec = new ReferenceTypeMap(coreMap.Library, coreMap);
			
			// read the core array size.  expect = 2.
			System.Byte arraySize = (System.Byte) tmis.ReadObject("u8");
			
			// now the core is confirmed we can add in the extensions.
			for (int x = 0; x < (int) arraySize; x++)
			{
				byte[] dict = (byte[]) tmis.ReadObject("dictionary.words");
				System.IO.MemoryStream bais = new System.IO.MemoryStream(dict);
				TypeInputStream dictDataIn = new TypeInputStream(bais, coreMap);
				SetMap(dictDataIn, coreMap, mapSpec);
			}
			
			return mapSpec;
		}
		
		// This reads the contents of the data dictionary and sets this
		// map up using the data contained.  It checks all data with the
		// internal library.
		private static TypeMap SetMap(TypeInputStream dictDataIn, ReferenceTypeMap coreMap, TypeMap mapSpec)
		{
			
			
			int size = ((System.UInt16) dictDataIn.ReadObject("U16"));
			Triple[] newTypes = new Triple[size];
			
			// Step 1.  Read all the types in.
			for (int x = 0; x < size; x++)
			{
				Triple newType = new Triple();
				newType.id = ((System.UInt16) dictDataIn.ReadObject("U16"));
				newType.name = ((System.String) dictDataIn.ReadObject("meta.name"));
				newType.structure = (byte[]) dictDataIn.ReadObject("dictionary.definition");
				newTypes[x] = newType;
			}
						
			// Step 2. Reserve unknown types and map types.
			for (int x = 0; x < size; x++)
			{
				if (coreMap.Library.GetTypeState(newTypes[x].name) == TypeLibrary.TYPE_NOT_DEFINED)
				{
					int res = coreMap.Library.Reserve(newTypes[x].name);
					newTypes[x].reserved = true;
					mapSpec.Map(newTypes[x].id, res);
				}
				else
				{
					newTypes[x].reserved = false;
					mapSpec.Map(newTypes[x].id, coreMap.Library.GetId(newTypes[x].name));
				}
			}
			
			// Step 3. Compare known types with resolved structures.
			for (int x = 0; x < size; x++)
			{
				if (!newTypes[x].reserved)
				{
					//System.out.println( "\n\nchecking same structure for " + newTypes[x].name );
					if (!mapSpec.IsSame(mapSpec.GetSystemId(newTypes[x].id), newTypes[x].name, newTypes[x].structure, coreMap))
					{
						throw new TypeException("type mismatch:" + newTypes[x].name);
					}
				}
			}
			
			// Step 3. Read structures.
			// Don't register until all structures are read and type names
			// are resolved.
			for (int x = 0; x < size; x++)
			{
				newTypes[x].definition = (MetaDefinition) mapSpec.ReadStructure(coreMap, newTypes[x].structure);
			}
			
			// Step 5. Register types with new structures.
			for (int x = 0; x < size; x++)
			{
				if (newTypes[x].reserved)
				{
					mapSpec.Library.Register(newTypes[x].name, newTypes[x].definition);
				}
			}
			
			return mapSpec;
		}
		
		private static byte[] WriteCore(TypeMapCore map)
		{
			// write out the core elements.
			System.Collections.IList coreIds = argot.TypeMapCore.CoreIdentifiers;
			System.IO.MemoryStream baos1 = new System.IO.MemoryStream();
			TypeOutputStream out1 = new TypeOutputStream(baos1, map);
			
			// write the number of entries.
			out1.WriteObject("U16", (System.Object) coreIds.Count);
			
			
			System.Collections.IEnumerator i = coreIds.GetEnumerator();
			while (i.MoveNext())
			{
				int id = ((System.Int32) i.Current);
				System.String name = map.GetName(id);
				MetaDefinition definition = (MetaDefinition) map.GetStructure(id);
				
				out1.WriteObject("U16", (System.Object) id);
				out1.WriteObject("meta.name", name);
				out1.WriteObject("dictionary.definition", definition);
			}
			
			out1.Stream.Close();
			baos1.Close();
			
			return baos1.ToArray();
		}
		
		public class Triple
		{
			public int id;
			public System.String name;
			public byte[] structure;
			public bool reserved;
			public MetaDefinition definition;
		}
	}
}
